/**
 *  Adds all  callbacks to the SweepViewFrame to make the whole thing run.
 *
 *  Copyright (c) 2009 Daniel Kornhauser.  All rights reserved.
 *  @author Daniel Kornhauser
 */

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;

import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;

import org.rosuda.JRI.REXP;

import widgets.CSVFileChooserFilter;
import widgets.NlogoFileChooserFilter;

import experiment.Protocol;
import experiment.ProtocolSaver;
import experiment.ValueSet;

import model.*;

public class SweepViewGui extends SweepViewFrame {
	private static final long serialVersionUID = 1L;

	/***** FIELD DECLARATIONS *****/

	final JFileChooser nlogoFileChooser = new JFileChooser();  // For opening a file
	final JFileChooser csvFileChooser = new JFileChooser();  // For opening a file

	File modelFile; // The file where the model is loaded
	ParameterHashMap parameterHashMap;

	// Variables for 
	//private int levels = 5; // The number of intervals between the min and max of the range of a widget
	private int dataSetCounter =1; // increments each run and is used for 

	// Object with their own classes 
	public static GDC gdc;
	public R r;

	// Models of widgets declared in Frame built with Netbeans
	DefaultListModel parameterListModel;
	DefaultComboBoxModel parameterComboModel;
	DefaultListModel metricListModel;
	DefaultComboBoxModel metricComboModel;
	SpinnerNumberModel runsModel;
	SpinnerNumberModel stepsModel;
	DefaultComboBoxModel parameterXModel;
	DefaultComboBoxModel parameterYModel;
	DefaultComboBoxModel metricXModel;
	DefaultComboBoxModel metricYModel;
	DefaultComboBoxModel groupModel;
	DefaultComboBoxModel heatmapModel;
	DefaultComboBoxModel subsetModel;
	DefaultTableModel tableModel ;
	HashMap <String, JCheckBox> scatterPlotCheckBoxHashMap;
	HashMap <String, JCheckBox>  distributionPlotCheckBoxHashMap;

	// Thread of behaviorspace.
	Process process;
	
	/***** CLASS CONSTRUCTOR AND INITIALIZERS *****/

	public SweepViewGui()
	{
		super();
	}

	public void initSweepView(R r)
	{
		this.r = r;
		initGUI();
		initGDC();
		initListeners();
	}
	
	
	private void initGDC()
	{
		// Create the canvas where R will draw the graphics
		gdc = new GDC(vizPanel.getWidth(), vizPanel.getHeight(), r.engine);
		vizPanel.add(gdc, BorderLayout.CENTER);
		vizPanel.setVisible(true);
		Dimension d = new Dimension(vizPanel.getWidth(), vizPanel.getHeight() );
		System.out.println(d);
		gdc.setSize(d);
		gdc.initRefresh();
		gdc.setVisible(true);
	}

	private void initGUI ()
	{
		// Setting the divider of the main Split Pane to the size of the Console
		consoleVizSplitPane.setDividerLocation(400);
		consoleVizSplitPane.setDividerLocation(0);
		
		// Bind View and Models
		parameterListModel = new DefaultListModel();
		parameterList.setModel(parameterListModel);
		parameterComboModel = new DefaultComboBoxModel();
		parameterComboBox.setModel(parameterComboModel);

		metricListModel = new DefaultListModel();
		metricList.setModel(metricListModel);
		metricComboModel = new DefaultComboBoxModel();
		metricComboBox.setModel(metricComboModel);

		runsModel = new SpinnerNumberModel(0, 0, null, 1);
		runsSpinner.setModel(runsModel);
		stepsModel = new SpinnerNumberModel(0, 0, null, 1);
		stepsSpinner.setModel(stepsModel);

		parameterXModel = new DefaultComboBoxModel();
		xyParameterXComboBox.setModel(parameterXModel);
		histogramParameterXComboBox.setModel(parameterXModel);
		densityParameterXComboBox.setModel(parameterXModel);
		boxWhiskerParameterXComboBox.setModel(parameterXModel);
		parameterYModel = new DefaultComboBoxModel();
		xyParameterYComboBox.setModel(parameterYModel);
		histogramParameterYComboBox.setModel(parameterYModel);
		densityParameterYComboBox.setModel(parameterYModel);
		boxWhiskerParameterYComboBox.setModel(parameterYModel);
		
		metricXModel = new DefaultComboBoxModel();
		xyMetricXComboBox.setModel(metricXModel);
		metricYModel = new DefaultComboBoxModel();
		xyMetricYComboBox.setModel(metricYModel);
		histogramMetricYComboBox.setModel(metricYModel);	
		densityMetricYComboBox.setModel(metricYModel);	
		boxWhiskerMetricYComboBox.setModel(metricYModel);	
		tableModel = (DefaultTableModel) dataSetTable.getModel();
		dataSetTable.removeColumn(dataSetTable.getColumnModel().getColumn(2));
		dataSetTable.setTableHeader(null);

		subsetModel = new DefaultComboBoxModel();
		subsetComboBox.setModel(subsetModel);

		groupModel = new DefaultComboBoxModel();
		xyGroupComboBox.setModel(groupModel);
		histogramGroupComboBox.setModel(groupModel);
		densityGroupComboBox.setModel(groupModel);
		boxWhiskerGroupComboBox.setModel(groupModel);
		
		heatmapModel = new DefaultComboBoxModel();
		heatmapModel.setSelectedItem("mean(y)");
		heatmapModel.setSelectedItem("min(y)");
		heatmapModel.setSelectedItem("max(y)");
		heatmapModel.setSelectedItem("sd(y)");
		heatmapModel.setSelectedItem("mean(y)");
		xyHeatmapComboBox.setModel(heatmapModel);
		histogramHeatmapComboBox.setModel(heatmapModel);
		boxWhiskerHeatmapComboBox.setModel(heatmapModel);
		
		// Initial Values
		metricComboModel.addElement("count turtles");
		metricComboModel.addElement("count patches");
		metricComboModel.addElement("count patches with [pcolor = green]");
		metricComboModel.addElement("count turtles with [color = blue]");
		metricComboModel.addElement("mean [size] of turtles");
		metricComboModel.addElement("max [size] of turtles");
		metricComboModel.addElement("min [size] of turtles");
		metricComboModel.addElement("sum [size / 2] of turtles");
		metricComboModel.addElement("length remove-duplicates [shape] of turtles");
		metricComboModel.addElement("[xcor] of turtle 0");
		metricComboModel.addElement("[ycor] of turtle 0");
		parameterXModel.addElement("");
		parameterXModel.setSelectedItem("");
		parameterYModel.addElement("");
		parameterYModel.setSelectedItem("");
		metricXModel.addElement("");
		metricXModel.setSelectedItem("");
		metricYModel.addElement("");
		metricYModel.setSelectedItem("");     

		scatterPlotCheckBoxHashMap = new HashMap<String,  JCheckBox>();
		distributionPlotCheckBoxHashMap = new HashMap<String, JCheckBox>();   
		
		CSVFileChooserFilter csvFileChooserFilter = new CSVFileChooserFilter();
		csvFileChooser.addChoosableFileFilter(csvFileChooserFilter);

		NlogoFileChooserFilter nlogoFileChooserFilter = new NlogoFileChooserFilter();
		nlogoFileChooser.addChoosableFileFilter(nlogoFileChooserFilter);
	}


	/***** LISTENERS*****/

	private void initListeners()
	{
		addParameterButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt) {
			addParameterButtonMouseClicked(evt);}});

		removeParameterButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt) {
			removeParameterButtonMouseClicked(evt);}});

		addMetricButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt){
			addMetricButtonMouseClicked(evt);}});

		removeMetricButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt){
			removeMetricButtonMouseClicked(evt);}});

		removeMetricButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt){
			removeMetricButtonMouseClicked(evt);}});
		
		loadCSVButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt){
			loadCSVButtonClicked(evt);}});
		
		removeDataFrameButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt){
			removeDataFrameButtonClicked(evt);}});
		
		runButton.addMouseListener(new java.awt.event.MouseAdapter()
		{ public void mouseClicked(java.awt.event.MouseEvent evt) {
			runButtonMouseClicked(evt); }});

		xyPlotButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt) {
			xyPlotButtonMouseClicked(evt); }});
		
		xyUpdateButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt) {
			xyPlotButtonMouseClicked(evt); }});
		
		histogramPlotButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt) {
			histogramPlotButtonMouseClicked(evt); }});

		densityPlotButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt) {
			densityPlotButtonMouseClicked(evt); }});

		boxWhiskerPlotButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt) {
			boxWhiskerPlotButtonMouseClicked(evt); }});
		
		scatterPlotButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt) {
			scaterplotButtonMouseClicked(evt); }});

		distributionPlotButton.addMouseListener(new java.awt.event.MouseAdapter()
		{public void mouseClicked(java.awt.event.MouseEvent evt) {
			distributionplotButtonMouseClicked(evt); }});

		openMenuItem.addActionListener(new java.awt.event.ActionListener()
		{public void actionPerformed(java.awt.event.ActionEvent evt){
			openMenuItemActionPerformed(evt);}});

		dataSetTable.getSelectionModel().addListSelectionListener(new ListSelectionListener()
		{public void valueChanged(ListSelectionEvent evt) {
			datasetRowSelected(evt);}});
	}

	/***** CALLBACKS *****/

	private void datasetRowSelected( ListSelectionEvent evt)
	{
		int selected_index  = ((javax.swing.DefaultListSelectionModel )evt.getSource()).getAnchorSelectionIndex();
		REXP  rexpSummary =  r.engine.eval("get_dataset_summary("+(selected_index+1)+")");

		String summaryString = "";
		for (String line : rexpSummary.asStringArray() )
		{
			line = line.replaceAll("^\\s+",  "+"); 
			line = line.replaceFirst("\"", "  ");
			line = line.replaceAll("\"",  "");
			summaryString =  summaryString+ line.substring(1, 22) + "\n";
		}
		summaryTextPane.setText(summaryString);
		summaryTextPane.setCaretPosition(1);
		// Updating Str
		REXP  rexpStr =  r.engine.eval("get_dataset_str("+(evt.getFirstIndex()+1)+")");   	
		String strString = "";
		for (String line : rexpStr.asStringArray() )
		{
			line = line.replaceFirst("\'data.frame\':\t",  "");
			line = line.replaceFirst("variables:",  "variables\n");
			line = line.replaceFirst("\"", "  ");
			line = line.replaceAll(":",  "\n  ");
			line = line.replaceAll("\\s\\$\\s",  " "); 
			if (line.length() > 45)
			{
				strString =  strString+ line.substring(1, 45) + "...\n";
			}
			else
			{
				strString =  strString+ line + "\n";
			}
		}
		strTextPane.setText(strString);
		strTextPane.setCaretPosition(1);
	}

	private void openMenuItemActionPerformed(ActionEvent evt) 
	{
		int returnVal = nlogoFileChooser.showOpenDialog(this);

		if (returnVal == JFileChooser.APPROVE_OPTION)
		{
			modelFile = nlogoFileChooser.getSelectedFile();
			populateRunAndPlotWidgets(modelFile);
		} 
		else { /*Canceled*/ }
	}
	
	
	private void removeDataFrameButtonClicked(java.awt.event.MouseEvent evt)
	{
		//TODO: Implement this !!!
	}
	
	private void loadCSVButtonClicked(java.awt.event.MouseEvent evt)
	{
		int returnVal = csvFileChooser.showOpenDialog(this);

		if (returnVal == JFileChooser.APPROVE_OPTION)
		{
			File dataSetFile = csvFileChooser.getSelectedFile();
			loadCSVFile(dataSetFile.getAbsolutePath(), "Dataset");
		} 
	}
	
	private void addParameterButtonMouseClicked(java.awt.event.MouseEvent evt)
	{
		String paramString = parameterComboBox.getSelectedItem().toString();
		String name = parameterHashMap.addParamSpec(paramString);

		int parExistsIndex = -1;
		for (int i=0; i<parameterListModel.getSize(); i++)
		{
			ParameterSpec parameterSpec = (ParameterSpec)parameterListModel.get(i);
			if (name.equals(parameterSpec.name))
			{
				parExistsIndex = i;
			}
		}

		if (parExistsIndex != -1) 
		{
			parameterListModel.remove(parExistsIndex);
			parameterListModel.add(parExistsIndex, parameterHashMap.get(name));
		}
		else
		{
			parameterListModel.addElement(parameterHashMap.get(name));
		}
	}

	private void removeParameterButtonMouseClicked(java.awt.event.MouseEvent evt)
	{
		parameterListModel.removeElement(parameterList.getSelectedValue());
	}    

	private void addMetricButtonMouseClicked(java.awt.event.MouseEvent evt)
	{      
		boolean metricExistsIndex = false;
		for (int i=0; i<metricListModel.getSize(); i++)
		{
			String metric = (String) metricListModel.get(i);
			if (metric.equals(metricComboBox.getSelectedItem()))
			{
				metricExistsIndex = true;
			}
		}

		if (!metricExistsIndex) 
		{
			metricListModel.addElement(metricComboBox.getSelectedItem());
		}
	}

	private void removeMetricButtonMouseClicked(java.awt.event.MouseEvent evt)
	{
		metricListModel.removeElement(metricList.getSelectedValue());
	}        

	
	private void populateMatrixPlotWidgets(String[] dataFrameNames, int paramMetricIdx)
	{
		
		// Finish updating Metrics
		// Note that we start the loop at 1 instead of 0 because it does not make any sense
		// including dataset as a Conditional Plot variable, we use it for grouping
		for (int i=0; i<paramMetricIdx; i++) 
		{  
			System.out.println(parameterXModel.getIndexOf(dataFrameNames[i]));
			if (parameterXModel.getIndexOf(dataFrameNames[i]) < 0)
			{
				parameterXModel.addElement(dataFrameNames[i]);
				parameterYModel.addElement(dataFrameNames[i]);
				groupModel.addElement(dataFrameNames[i]);

			}
			
		}

		for (int i=paramMetricIdx; i<dataFrameNames.length; i++)
		{  
			if (metricXModel.getIndexOf(dataFrameNames[i]) < 0)
			{
				metricXModel.addElement(dataFrameNames[i]);
				metricYModel.addElement(dataFrameNames[i]);
			}
		}		
	}

	private void populateScatterPlotWidgets(String[] dataFrameNames)
	{
		scatterPlotCheckBoxPanel.removeAll();
		HashMap<String, JCheckBox>newScatterPlotCheckBoxHashMap = new HashMap<String, JCheckBox>();
		for (int i=0; i<dataFrameNames.length; i++)
		{  
			//System.out.print(dataFrameNames[i] + "  --  "); 
			JCheckBox checkBox;

			// If there are checkbox with previous values we respect them.
			if (scatterPlotCheckBoxHashMap.get(dataFrameNames[i]) != null)
			{
				Boolean checkBoxSelected = scatterPlotCheckBoxHashMap.get(dataFrameNames[i]).isSelected();
				checkBox = new JCheckBox(dataFrameNames[i],  checkBoxSelected);   	     			
			}
			// If this is the first time we do not check the checkboxes with "X.run.number. ", "X.step."
			else
			{
				if ( dataFrameNames[i].equals("X.step."))
				{
					checkBox = new JCheckBox(dataFrameNames[i],  true);
				}
				// but we check all the other boxes
				else 
				{ 
					checkBox = new JCheckBox(dataFrameNames[i],  false);
				}
			}
			checkBox.setName(dataFrameNames[i]);
			scatterPlotCheckBoxPanel. add(checkBox);
			newScatterPlotCheckBoxHashMap.put(dataFrameNames[i], checkBox);    
		}
		scatterPlotCheckBoxHashMap = newScatterPlotCheckBoxHashMap; 				
	}

	private void populateDistributionPlotWidgets(String[] dataFrameNames)
	{
		distributionPlotCheckBoxPanel.removeAll();
		HashMap<String, JCheckBox>newDistributionPlotCheckBoxHashMap = new HashMap<String, JCheckBox>();
		for (int i=0; i<dataFrameNames.length; i++)
		{  
			//System.out.print(dataFrameNames[i] + "  --  "); 
			JCheckBox checkBox;

			// If there are checkbox with previous values we respect them.
			if (distributionPlotCheckBoxHashMap.get(dataFrameNames[i]) != null)
			{
				Boolean checkBoxSelected = distributionPlotCheckBoxHashMap.get(dataFrameNames[i]).isSelected();
				checkBox = new JCheckBox(dataFrameNames[i],  checkBoxSelected);   	     			
			}
			// If this is the first time we do not check the checkboxes with "X.run.number. ", "X.step."
			else
			{
				if (dataFrameNames[i].equals("X.step."))
				{
					checkBox = new JCheckBox(dataFrameNames[i],  true);
				}
				// but we check all the other boxes
				else 
				{ 
					checkBox = new JCheckBox(dataFrameNames[i],  false);
				}
			}
			checkBox.setName(dataFrameNames[i]);
			distributionPlotCheckBoxPanel. add(checkBox);
			newDistributionPlotCheckBoxHashMap.put(dataFrameNames[i], checkBox);    
		}
		distributionPlotCheckBoxHashMap = newDistributionPlotCheckBoxHashMap; 						
	}
	
	
	private void runButtonMouseClicked(java.awt.event.MouseEvent evt)
	{
		

		if (((JButton)evt.getSource()).getText().equals("Go"))
		{
		Protocol protocol =    createProtocol();
		saveProtocol(protocol);
		executeNetLogo();
		}
		else if (((JButton)evt.getSource()).getText().equals("Stop"))
		{
			process.destroy();
		}
			
	}
	
	public void loadCSVFile(String fileName, String dataSetName)
		{		
		
		r.engine.eval("load_dat(\""+fileName+"\","+dataSetCounter+")");
		//r.engine.eval("print(my.dat)"); 

		REXP  rexp = r.engine.eval("names(my.dat)");
		String dataFrameNames[]=rexp.asStringArray();
		System.out.println("dataFrameNames:");
		System.out.println(java.util.Arrays.deepToString(dataFrameNames));


		// FIXME: There must be some better way of searching a string in a string array ...
		// but I could not find it.
		int paramMetricIdx = 0;
		System.out.println(dataFrameNames.length);
		for (int i=0; i<dataFrameNames.length; i++)
		{  
			System.out.println(i);
			if (dataFrameNames[i].equals("X.step."))
			{
				paramMetricIdx = i;
				break;
			}
		}

		//r.engine.eval("plot_viz_splom("+(paramMetricIdx + 1)+")");
		
		populateMatrixPlotWidgets(dataFrameNames, paramMetricIdx);
		populateScatterPlotWidgets(dataFrameNames);
		populateDistributionPlotWidgets(dataFrameNames);
		
		Dimension d = new Dimension(vizPanel.getWidth(), vizPanel.getHeight() );
		gdc.setSize(d);
		gdc.initRefresh();

		//Finish updating UI
		tableModel.addRow(new Object[]{Boolean.TRUE, 
				new String(dataSetName + dataSetCounter ), 
				new Integer(dataSetCounter)});
		dataSetCounter++;        
	}

	private void subsetDataset()
	{
		// Subset runs
		final int SELECTED_COLUMN = 0;
		final int NAME_COLUMN = 1;
		final int ID_COLUMN = 2;
		
		Object data[] =  tableModel.getDataVector().toArray();
		System.out.println(java.util.Arrays.deepToString(data));
		
		String selectedDataSets= "";
		for (int i = 0; i < data.length; i++) 
		{
			Vector row = (Vector) data[i];
			if ((Boolean)row.get(SELECTED_COLUMN))
			{
				selectedDataSets = selectedDataSets +row.get(ID_COLUMN) + ", " ;
			}
		}
			
		if (selectedDataSets.length() > 0)
		{
			// Taking out the extra comma
			selectedDataSets =  selectedDataSets.substring(0, selectedDataSets.length() - 2); 
		}
		System.out.println("subset_dataset(c("+ selectedDataSets+"))");
		r.engine.eval("subset_dataset(c("+ selectedDataSets+"))");
	}
	
	private void subsetCustom()
	{	
		// Subset custom
		String subset_additional = "";    	
		if (!(subsetComboBox.getEditor().getItem()).equals(""))
		{
			subset_additional =  subsetComboBox.getEditor().getItem().toString();
			r.engine.eval("subset_custom(\"my.selected_dat$"+subset_additional+"\")");
		}
	}
	
	
	private void xyPlotButtonMouseClicked(java.awt.event.MouseEvent evt)
	{
		System.out.println("xyPlot");
		System.out.println("set_manual_options( '" +transparencySlider.getValue() +"','"+
				scalesCheckBox.isSelected()+"')");
		r.engine.eval("set_manual_options( '" +transparencySlider.getValue() +"','"+
																			scalesCheckBox.isSelected()+"')");

		// the line below is only for debugging purposes, I reload the R script in case I changed
		// it between plotting in behvior explorer.
		// r.engine.eval("source('callbacks.R')	");

		// Get variables to plot
		String parameterX = (String) parameterXModel.getSelectedItem();
		String parameterY = (String) parameterYModel.getSelectedItem();
		String metricX = (String) metricXModel.getSelectedItem();
		String metricY = (String) metricYModel.getSelectedItem();
		String groupName = (String) xyGroupComboBox.getSelectedItem();
		String heatmap = (String) xyHeatmapComboBox.getSelectedItem();
		
		// Subset dataset
		subsetDataset();

		// Subset custom
		if (runButton.getText().equals("Go"))
		{
			subsetCustom();
		}
		else if (runButton.getText().equals("Stop"))
		{
			r.engine.eval("load_incomplete_dat(\"experiment.csv\")");
		}
		
		// EMPTY GRID
		if (metricX.equals("") & metricY.equals("") & parameterX.equals("") & !parameterY.equals(""))
		{
			// if no metric is chose just plot the grid
			System.out.println("Empty grid:  " + "plot_viz('X.step.', 'dummy',"+"'"+parameterX+"','"+parameterY+"','"+groupName +"')");
			r.engine.eval("plot_viz('X.step.', 'dummy',"+"'"+parameterX+"','"+parameterY+"','"+groupName +"')");
		}
		// ROW PLOT
		else  if (!metricX.equals("")  & !metricY.equals("") & !parameterX.equals("") &  parameterY.equals(""))
		{
			// if both metrics are chosen plot the x y-plot
			System.out.println("X Full Grid: " + "plot_viz_x('"+metricX+"','"+metricY+"','"+parameterX+"','" +groupName +"','"+heatmap +"')");
			r.engine.eval("plot_viz_x('"+metricX+"','"+metricY+"','"+parameterX+"','" +groupName +"','"+heatmap +"')");
		}  
		// MATRIX PLOT
		else  if (!metricX.equals("")  & !metricY.equals("") & !parameterX.equals("") &  !parameterY.equals(""))
		{
			// if both metrics are chosen plot the x y-plot
			System.out.println("XY Full Grid: " + "plot_viz('"+metricX+"','"+metricY+"','"+parameterX+"','"+parameterY+"','"+groupName+"','" +heatmap+"')");
			r.engine.eval("plot_viz('"+metricX+"','"+metricY+"','"+parameterX+"','"+parameterY+"','"+groupName+"','" +heatmap+"')");
		}
		// SUPERPOSED PLOT
		else  if (!metricX.equals("")  & !metricY.equals("") & parameterX.equals("") &  parameterY.equals(""))
		{
			// if both metrics but no parameters plot all off the metrics superposed
			//TODO: Plot all the metrics superposed
			System.out.println("Superposed: " + "plot_viz_superposed('"+metricX+"','"+metricY+"','"+groupName+"')");
			r.engine.eval("plot_viz_superposed('"+metricX+"','"+metricY+"','"+groupName+"')");
		}

		//TODO: What happens in only one metric is chosen ? Plot a histogram ?

		Dimension d = new Dimension(vizPanel.getWidth(),vizPanel.getHeight());
		gdc.setSize(d);
		gdc.initRefresh();
	}
	
	
	private void histogramPlotButtonMouseClicked(java.awt.event.MouseEvent evt)
	{
		System.out.println("Histogram");
		System.out.println("set_manual_options( '" +transparencySlider.getValue() +"','"+
				scalesCheckBox.isSelected()+"')");
		r.engine.eval("set_manual_options( '" +transparencySlider.getValue() +"','"+
																			scalesCheckBox.isSelected()+"')");

		// the line below is only for debugging purposes, I reload the R script in case I changed
		// it between plotting in behvior explorer.
		// r.engine.eval("source('callbacks.R')	");

		// Get variables to plot
		String parameterX = (String) parameterXModel.getSelectedItem();
		String parameterY = (String) parameterYModel.getSelectedItem();
		String metricX = (String) metricXModel.getSelectedItem();
		String metricY = (String) metricYModel.getSelectedItem();
		String groupName = (String) xyGroupComboBox.getSelectedItem();
		String heatmap = (String) xyHeatmapComboBox.getSelectedItem();

		// Subset dataset
		subsetDataset();

		// Subset custom
		if (runButton.getText().equals("Go"))
		{
			subsetCustom();
		}
		else if (runButton.getText().equals("Stop"))
		{
			r.engine.eval("load_incomplete_dat(\"experiment.csv\")");
		}
		
		// EMPTY GRID
		if (metricX.equals("") & metricY.equals("") & parameterX.equals("") & !parameterY.equals(""))
		{
			// if no metric is chose just plot the grid
			System.out.println("Empty grid:  " + "plot_viz('X.step.', 'dummy',"+"'"+parameterX+"','"+parameterY+"','"+groupName +"')");
			r.engine.eval("plot_viz('X.step.', 'dummy',"+"'"+parameterX+"','"+parameterY+"','"+groupName +"')");
		}
		// ROW PLOT
		else  if ( !metricY.equals("") & !parameterX.equals("") &  parameterY.equals(""))
		{
			// if both metrics are chosen plot the x y-plot
			System.out.println("X Full Grid: " + "plot_viz_x_histogram('"+metricY+"','"+parameterX+"','" +groupName +"','"+heatmap +"')");
			r.engine.eval("plot_viz_x_histogram('"+metricY+"','"+parameterX+"','" +groupName +"','"+heatmap +"')");
		}  
		// MATRIX PLOT
		else  if (!metricY.equals("") & !parameterX.equals("") &  !parameterY.equals(""))
		{
			// if both metrics are chosen plot the x y-plot
			System.out.println("XY Full Grid: " + "plot_viz_histogram('"+metricY+"','"+parameterX+"','"+parameterY+"','"+groupName+"','" +heatmap+"')");
			r.engine.eval("plot_viz_histogram('"+metricY+"','"+parameterX+"','"+parameterY+"','"+groupName+"','" +heatmap+"')");
		}
		// SUPERPOSED PLOT
		else  if ( !metricY.equals("") & parameterX.equals("") &  parameterY.equals(""))
		{
			// if both metrics but no parameters plot all off the metrics superposed
			//TODO: Plot all the metrics superposed
			System.out.println("Superposed: " + "plot_viz_superposed_histogram('"+metricY+"','"+groupName+"')");
			r.engine.eval("plot_viz_superposed_histogram('"+metricY+"','"+groupName+"')");
		}

		//TODO: What happens in only one metric is chosen ? Plot a histogram ?

		Dimension d = new Dimension(vizPanel.getWidth(),vizPanel.getHeight());
		gdc.setSize(d);
		gdc.initRefresh();
	}
	

	private void densityPlotButtonMouseClicked(java.awt.event.MouseEvent evt)
	{
		System.out.println("Density");
		System.out.println("set_manual_options( '" +transparencySlider.getValue() +"','"+
				scalesCheckBox.isSelected()+"')");
		r.engine.eval("set_manual_options( '" +transparencySlider.getValue() +"','"+
																			scalesCheckBox.isSelected()+"')");

		// the line below is only for debugging purposes, I reload the R script in case I changed
		// it between plotting in behvior explorer.
		// r.engine.eval("source('callbacks.R')	");

		// Get variables to plot
		String parameterX = (String) parameterXModel.getSelectedItem();
		String parameterY = (String) parameterYModel.getSelectedItem();
		String metricX = (String) metricXModel.getSelectedItem();
		String metricY = (String) metricYModel.getSelectedItem();
		String groupName = (String) xyGroupComboBox.getSelectedItem();
		String heatmap = (String) xyHeatmapComboBox.getSelectedItem();

		// Subset dataset
		subsetDataset();

		// Subset custom
		if (runButton.getText().equals("Go"))
		{
			subsetCustom();
		}
		else if (runButton.getText().equals("Stop"))
		{
			r.engine.eval("load_incomplete_dat(\"experiment.csv\")");
		}
		
		// EMPTY GRID
		if (metricX.equals("") & metricY.equals("") & parameterX.equals("") & !parameterY.equals(""))
		{
			// if no metric is chose just plot the grid
			System.out.println("Empty grid:  " + "plot_viz('X.step.', 'dummy',"+"'"+parameterX+"','"+parameterY+"','"+groupName +"')");
			r.engine.eval("plot_viz('X.step.', 'dummy',"+"'"+parameterX+"','"+parameterY+"','"+groupName +"')");
		}
		// ROW PLOT
		else  if ( !metricY.equals("") & !parameterX.equals("") &  parameterY.equals(""))
		{
			// if both metrics are chosen plot the x y-plot
			System.out.println("X Full Grid: " + "plot_viz_x_densityplot('"+metricY+"','"+parameterX+"','" +groupName +"','"+heatmap +"')");
			r.engine.eval("plot_viz_x_densityplot('"+metricY+"','"+parameterX+"','" +groupName +"','"+heatmap +"')");
		}  
		// MATRIX PLOT
		else  if (!metricY.equals("") & !parameterX.equals("") &  !parameterY.equals(""))
		{
			// if both metrics are chosen plot the x y-plot
			System.out.println("XY Full Grid: " + "plot_viz_densityplot('"+metricY+"','"+parameterX+"','"+parameterY+"','"+groupName+"','" +heatmap+"')");
			r.engine.eval("plot_viz_densityplot('"+metricY+"','"+parameterX+"','"+parameterY+"','"+groupName+"','" +heatmap+"')");
		}
		// SUPERPOSED PLOT
		else  if ( !metricY.equals("") & parameterX.equals("") &  parameterY.equals(""))
		{
			// if both metrics but no parameters plot all off the metrics superposed
			//TODO: Plot all the metrics superposed
			System.out.println("Superposed: " + "plot_viz_superposed_densityplot('"+metricY+"','"+groupName+"')");
			r.engine.eval("plot_viz_superposed_densityplot('"+metricY+"','"+groupName+"')");
		}

		//TODO: What happens in only one metric is chosen ? Plot a histogram ?

		Dimension d = new Dimension(vizPanel.getWidth(),vizPanel.getHeight());
		gdc.setSize(d);
		gdc.initRefresh();
	}
	

	private void boxWhiskerPlotButtonMouseClicked(java.awt.event.MouseEvent evt)
	{
		System.out.println("Density");
		System.out.println("set_manual_options( '" +transparencySlider.getValue() +"','"+
				scalesCheckBox.isSelected()+"')");
		r.engine.eval("set_manual_options( '" +transparencySlider.getValue() +"','"+
																			scalesCheckBox.isSelected()+"')");

		// the line below is only for debugging purposes, I reload the R script in case I changed
		// it between plotting in behvior explorer.
		// r.engine.eval("source('callbacks.R')	");

		// Get variables to plot
		String parameterX = (String) parameterXModel.getSelectedItem();
		String parameterY = (String) parameterYModel.getSelectedItem();
		String metricX = (String) metricXModel.getSelectedItem();
		String metricY = (String) metricYModel.getSelectedItem();
		String groupName = (String) xyGroupComboBox.getSelectedItem();
		String heatmap = (String) xyHeatmapComboBox.getSelectedItem();

		// Subset dataset
		subsetDataset();

		// Subset custom
		if (runButton.getText().equals("Go"))
		{
			subsetCustom();
		}
		else if (runButton.getText().equals("Stop"))
		{
			r.engine.eval("load_incomplete_dat(\"experiment.csv\")");
		}
		
		// EMPTY GRID
		if (metricX.equals("") & metricY.equals("") & parameterX.equals("") & !parameterY.equals(""))
		{
			// if no metric is chose just plot the grid
			System.out.println("Empty grid:  " + "plot_viz('X.step.', 'dummy',"+"'"+parameterX+"','"+parameterY+"','"+groupName +"')");
			r.engine.eval("plot_viz('X.step.', 'dummy',"+"'"+parameterX+"','"+parameterY+"','"+groupName +"')");
		}
		// ROW PLOT
		else  if ( !metricY.equals("") & !parameterX.equals("") &  parameterY.equals(""))
		{
			// if both metrics are chosen plot the x y-plot
			System.out.println("X Full Grid: " + "plot_viz_x_bwplot('"+metricY+"','"+parameterX+"','" +groupName +"','"+heatmap +"')");
			r.engine.eval("plot_viz_x_bwplot('"+metricY+"','"+parameterX+"','" +groupName +"','"+heatmap +"')");
		}  
		// MATRIX PLOT
		else  if (!metricY.equals("") & !parameterX.equals("") &  !parameterY.equals(""))
		{
			// if both metrics are chosen plot the x y-plot
			System.out.println("XY Full Grid: " + "plot_viz_bwplot('"+metricY+"','"+parameterX+"','"+parameterY+"','"+groupName+"','" +heatmap+"')");
			r.engine.eval("plot_viz_bwplot('"+metricY+"','"+parameterX+"','"+parameterY+"','"+groupName+"','" +heatmap+"')");
		}
		// SUPERPOSED PLOT
		else  if ( !metricY.equals("") & parameterX.equals("") &  parameterY.equals(""))
		{
			// if both metrics but no parameters plot all off the metrics superposed
			//TODO: Plot all the metrics superposed
			System.out.println("Superposed: " + "plot_viz_superposed_bwplot('"+metricY+"','"+groupName+"')");
			r.engine.eval("plot_viz_superposed_bwplot('"+metricY+"','"+groupName+"')");
		}

		//TODO: What happens in only one metric is chosen ? Plot a histogram ?

		Dimension d = new Dimension(vizPanel.getWidth(),vizPanel.getHeight());
		gdc.setSize(d);
		gdc.initRefresh();
	}
	
	
	private void scaterplotButtonMouseClicked(java.awt.event.MouseEvent evt)
	{
		
		// Subset dataset
		subsetDataset();
		
		// Subset custom
		subsetCustom();
		
		System.out.println("2" + scatterPlotCheckBoxHashMap);
		String selectedCols = new String();
		// WE can either get the name using the HashMap key or getting it directly from
		// the checkbox.
		// Maybe using the key would be cleaner.

		for (JCheckBox checkBox : scatterPlotCheckBoxHashMap.values()) 
		{
			if (checkBox.isSelected())
			{
			selectedCols = selectedCols +" \"" + checkBox.getName() + "\" ," ;
			}
		}	
		System.out.println(selectedCols.substring(0, selectedCols.length() - 1));
		r.engine.eval("my.test()");
		System.out.println("plot_viz_splom_names(c("+selectedCols.substring(0, selectedCols.length() - 1)+"))");
		r.engine.eval("plot_viz_splom_names(c("+selectedCols.substring(0, selectedCols.length() - 1)+"))");
	}

	
	private void distributionplotButtonMouseClicked(java.awt.event.MouseEvent evt)
	{
		// Subset dataset
		subsetDataset();
		
		// Subset custom
		subsetCustom();
		
		System.out.println("2" + distributionPlotCheckBoxHashMap);
		String selectedCols = new String();
		// WE can either get the name using the HashMap key or getting it directly from
		// the checkbox.
		// Maybe using the key would be cleaner.

		for (JCheckBox checkBox : distributionPlotCheckBoxHashMap.values()) 
		{
			if (checkBox.isSelected())
			{
			selectedCols = selectedCols +" \"" + checkBox.getName() + "\" ," ;
			}
		}	
		System.out.println(selectedCols.substring(0, selectedCols.length() - 1));
		r.engine.eval("my.test()");
		System.out.println("plot_viz_marginals(c("+selectedCols.substring(0, selectedCols.length() - 1)+"))");
		r.engine.eval("plot_viz_marginals(c("+selectedCols.substring(0, selectedCols.length() - 1)+"))");
	}
	

	/***** CALLBACK HELPER FUNCTIONS*****/

	private void populateRunAndPlotWidgets(File file)
	{
		parameterHashMap = ModelReader.getParameterHashMap(file);
		for (ParameterSpec parameterSpec : parameterHashMap.values())
		{
			parameterComboBox.addItem(parameterSpec);
		}      
	}    

	private void executeNetLogo()
	{
		   class NetLogoRunnable implements Runnable {
			      private JButton runButton;
			      public NetLogoRunnable(JButton runButton) {
			          this.runButton = runButton;
			       }

			public void run()
			{
				try
				{
				runButton.setText("Stop");
				File wd = new File("netlogo");

				System.out.println("modelFile.getAbsolutePath()" + modelFile.getAbsolutePath());
				System.out.println("modelFile.getCanonicalPath()" + modelFile.getCanonicalPath());

				String cmd  =   "java -server -Xmx1024M -cp NetLogo.jar " +
				//"org.nlogo.headless.HeadlessWorkspace "  + // For NetLogo 4.0 keeping it for benchmarks
				"org.nlogo.headless.Main "  +
				" --setup-file ../experiment.xml " +
				" --model " + modelFile.getAbsolutePath() + // ../Wolf.nlogo "
				" --experiment experiment "  +
				" --table ../experiment.csv " ;

				System.out.println(cmd);

				String s = null;
				process = Runtime.getRuntime().exec(cmd,null, wd);
							
				BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
				BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));

				// read the output from the command
				//System.out.println("Here is the standard output of the command:\n");
				while ((s = stdInput.readLine()) != null) {System.out.println(s);}

				// read any errors from the attempted command
				System.out.println("Here is the standard error of the command (if any):\n");
				while ((s = stdError.readLine()) != null) {System.out.println(s); }
				}
				catch (IOException e)
				{
					System.out.println("exception happened - here's what I know: ");
					//e.printStackTrace();
				}

				System.out.println("__________________");
				try {
					System.out.println(process.waitFor());
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("process.exitValue()");
				System.out.println(process.exitValue());

				String dataSetName = "";
				if (process.exitValue() == 0)
				{
					dataSetName = "Dataset";
				}
				else
				{
					dataSetName = "Partial Dataset";					
				}
				
				loadCSVFile("experiment.csv", dataSetName);
				runButton.setText("Go");
			}
		}


		Thread netlogoThread = new Thread(new NetLogoRunnable(runButton));
		netlogoThread.start();
		//TODO: Finish implementing this: 
		//              - Load in background
		//              - Avoid updating datasets
		//              - New Interaction with datasets must be done before continuing.
		//Thread lineCounter = new Thread(new LineCounter(this));
		//lineCounter.start();
	}

	private void saveProtocol(Protocol protocol)
	{
		// Save the protocol
		ArrayList<Protocol> protocolList = new ArrayList<Protocol>();
		protocolList.add(protocol);
		String protocolBody = ProtocolSaver.save(protocolList, "experiment.xml");
	}
	private Protocol createProtocol()
	{
		Protocol protocol = new Protocol();
		protocol.name = "experiment";
		protocol.repetitions = runsModel.getNumber().intValue()  ;
		System.out.println("@@@@@@@@@@@repetitions" + runsModel.getNumber().intValue());
		protocol.hasTimeLimit = true;
		System.out.println("@@@@@@@@@@@timeLimit" + stepsModel.getNumber().intValue());
		protocol.timeLimit = stepsModel.getNumber().intValue();
		protocol.setupCommands = "setup";
		protocol.goCommands = "go";
		
		 if (! stopConditionTextField.getText().trim().equals(""))
		 {
			 	protocol.hasExitCondition = true ;
				protocol.exitCondition = stopConditionTextField.getText().trim() ;			 
		 }
		 else
		 {
			 	protocol.hasExitCondition = false ;			 
		 }


		String nameX =  (String) xyParameterXComboBox.getSelectedItem();
		String nameY =  (String) xyParameterYComboBox.getSelectedItem();
		for(int i = 0; i < parameterList.getModel().getSize(); i++)
		{
			ValueSet valueSet = new ValueSet();
			valueSet.isStepped = true;
			//TODO: Eventually we will generallize the line below with other types of data
			DoubleDiscreteSpec sliderSpec= (DoubleDiscreteSpec) parameterList.getModel().getElementAt(i);

			valueSet.first = sliderSpec.dMin;
			valueSet.last = sliderSpec.dMax;  
			valueSet.step = sliderSpec.dStep;

			// TODO: Add a button to Levels
			protocol.valueSets.put(sliderSpec.name, valueSet);
		}

		String[] metricStringArray = new String[metricList.getModel().getSize()];
		for (int i=0; i<metricList.getModel().getSize(); i++) {
			metricStringArray[i] = (String) metricList.getModel().getElementAt(i);
		}
		protocol.metrics= metricStringArray;

		return protocol;
	}

	/***** OTHER FUNCTIONS*****/

	public void autoLoad() 
	{
//		modelFile = new File("/Applications/NetLogo 4.0.4/models/Sample Models/Chemistry & Physics/Chemical Reactions/Simple Kinetics 1.nlogo");
//		populateRunAndPlotWidgets(modelFile);
//		metricListModel.addElement("count reactants");
//		metricListModel.addElement("count products");
//		parameterListModel.addElement(new DoubleDiscreteSpec("Kb",0 ,33, 100));
//		parameterListModel.addElement(new DoubleDiscreteSpec("Ku",0 ,33, 100));
//		stepsModel.setValue(200);
//		runsModel.setValue(1);

//		modelFile = new File("wolf.nlogo");
//		populateRunAndPlotWidgets(modelFile);
//		metricListModel.addElement("count sheep");
//		this.xyParameterXComboBox.setSelectedItem("initial.number.sheep");
//		this.xyParameterYComboBox.setSelectedItem("initial.number.wolves");
//		this.xyMetricXComboBox.setSelectedItem("X.step.");
//		this.xyMetricYComboBox.setSelectedItem("count.sheep");
//		parameterListModel.addElement(new DoubleDiscreteSpec("initial-number-sheep", 0, 33, 100));
//		parameterListModel.addElement(new DoubleDiscreteSpec("initial-number-wolves", 0, 33, 100));
//		stepsModel.setValue(20);
//		runsModel.setValue(1);
		
		modelFile = new File("wolf.nlogo");
		populateRunAndPlotWidgets(modelFile);
		metricListModel.addElement("count sheep");
		metricListModel.addElement("count wolves");
		//metricListModel.addElement("count patches with [pcolor = green] / 4");
		parameterListModel.addElement(new DoubleDiscreteSpec("initial-number-sheep",0 ,33, 100));
		parameterListModel.addElement(new DoubleDiscreteSpec("initial-number-wolves",0 ,33, 100));
		stepsModel.setValue(10);
		runsModel.setValue(1);

		//loadCSVFile("dataset_wolf-sheep_500steps_[0,10,100]-count_sheep_wolves_grass.csv");

		//loadCSVFile("dataset_wolf-sheep_20steps_[0,33,100]-count_sheep_wolves_grass.csv", "Dataset");
		//loadCSVFile("dataset_wolf-sheep_20steps_[0,33,100]-count_sheep_wolves_grass_bis.csv", "Dataset");
		//modelFile = new File("Fire.nlogo");
		//populateRunAndPlotWidgets(modelFile);
		//metricListModel.addElement("percent-burned");
		//parameterListModel.addElement(new DoubleDiscreteSpec("density",0,33,99));
		//stepsModel.setValue(10);
		//runsModel.setValue(1);
		//heatmapModel.setSelectedItem("mean(panel$y)/max(y)");
		//heatmapModel.addElement("mean(panel$y)/max(y)");
		//heatmapModel.addElement("max(panel$y)/max(y)");
		//groupModel.addElement("dataset");

		//r.engine.eval("init_dat('INITIAL-NUMBER-WOLVES'," +
		//                       0 +", " + 150 +", " + 75 +" ," +
		//                       "'INITIAL-NUMBER-SHEEP'," +
		//                       0 +", " + 150 +", " + 75 +")");
		//r.engine.eval("print(dir())");
		//r.engine.eval("load_dat(\"cell-output.csv\")");
		//r.engine.eval("print(my.dat)");
		//r.engine.eval("plot_viz('X.step.', 'count.turtles', 'INITIAL.NUMBER.WOLVES', 'INITIAL.NUMBER.SHEEP')");
	}
}
